package master.ghrm.domain;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.MapKey;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Version;

import org.hibernate.annotations.Type;
import org.joda.time.DateTime;

@Entity
public class Evaluation extends AbstractBaseEntity {

	private static final long serialVersionUID = 1L;

	@Version
	private Long version;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "evaluation_id")
	private Integer evaluationId;

	@Column(name = "eval_date")
	@Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
	private DateTime evalDate;

	private Boolean published = false;
	private Boolean finished = false;

	@ManyToOne(fetch = FetchType.EAGER)
	@JoinColumn(name = "questioneer_id", nullable = false)
	private Questioneer questioneer;

	@OneToOne(fetch = FetchType.LAZY, optional = true)
	@JoinColumn(name = "evaluee_id")
	private Profile evaluee;

	@ManyToOne(fetch = FetchType.EAGER)
	@JoinColumn(name = "evaluation_session_id", nullable = false)
	private EvaluationSession evaluationSession;

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "evaluation")
	private List<Assessment> assessments = new ArrayList<Assessment>(0);

	@ManyToMany(fetch = FetchType.LAZY)
	@JoinTable(name = "tbl_evaluation_evaluator", joinColumns = { @JoinColumn(name = "evaluation_id", nullable = true, updatable = true) }, inverseJoinColumns = { @JoinColumn(name = "evaluator_id", nullable = true, updatable = true) })
	private List<Evaluator> interviewers = new ArrayList<Evaluator>(0);

	@Column(name = "event_id")
	private Long eventId;

	@OneToMany(mappedBy = "id.evaluation")
	@MapKey(name = "id.category")
	private Map<EvalCategory, PDPReminder> pdpReminders;

	/**
	 * Default constructor for persistence.
	 */
	public Evaluation() {
	}

	/**
	 * @param anEvalDate evaluation date.
	 * @param aQuestioneer questioneer this evaluation is based on.
	 */
	public Evaluation(DateTime anEvalDate, Questioneer aQuestioneer) {
		super();
		this.evalDate = anEvalDate;
		this.questioneer = aQuestioneer;
	}

	/**
	 * @return optimistic locking version.
	 */
	public Long getVersion() {
		return version;
	}

	/**
	 * @param aVersion optimistic locking version.
	 */
	void setVersion(Long aVersion) {
		this.version = aVersion;
	}

	/**
	 * @return evaluation ID.
	 */
	public Integer getEvaluationId() {
		return evaluationId;
	}

	/**
	 * @param anEvaluationId evaluation ID.
	 */
	public void setEvaluationId(Integer anEvaluationId) {
		this.evaluationId = anEvaluationId;
	}

	/**
	 * @return evaluation date.
	 */
	public DateTime getEvalDate() {
		return evalDate;
	}

	/**
	 * @param anEvalDate evaluation date to set.
	 */
	public void setEvalDate(DateTime anEvalDate) {
		this.evalDate = anEvalDate;
	}

	/**
	 * @return evalDate hour
	 */
	public int getEvalHour() {
		int hour = 0;
		if (evalDate != null) {
			hour = evalDate.getHourOfDay();
		}

		return hour;
	}

	/**
	 * @return evalDate minute
	 */
	public int getEvalMinute() {
		int minute = 0;
		if (evalDate != null) {
			minute = evalDate.getMinuteOfHour();
		}

		return minute;
	}

	/**
	 * @return if the evaluation is published.
	 */
	public Boolean getPublished() {
		return published;
	}

	/**
	 * @param isPublished is the evaluation is published.
	 */
	public void setPublished(Boolean isPublished) {
		this.published = isPublished;
	}

	/**
	 * @return if the evaluation is finished.
	 */
	public Boolean getFinished() {
		return finished;
	}

	/**
	 * @param isFinished is the evaluation is finished.
	 */
	public void setFinished(Boolean isFinished) {
		this.finished = isFinished;
	}

	/**
	 * @return questioneer.
	 */
	public Questioneer getQuestioneer() {
		return questioneer;
	}

	/**
	 * @param aQuestioneer questioneer to set.
	 */
	public void setQuestioneer(Questioneer aQuestioneer) {
		this.questioneer = aQuestioneer;
	}

	/**
	 * @return assessments.
	 */
	public List<Assessment> getAssessments() {
		return assessments;
	}

	/**
	 * @param assessmentList assessments to set.
	 */
	public void setAssessments(List<Assessment> assessmentList) {
		this.assessments = assessmentList;
	}

	/**
	 * @return number of assessments.
	 */
	public int getNbAssessments() {
		return assessments.size();
	}

	/**
	 * @return number of finished assessments.
	 */
	public int getNbFinishedAssessments() {
		int nbFinished = 0;
		for (final Assessment assessment : assessments) {
			if (assessment.getPublished()) {
				nbFinished++;
			}
		}

		return nbFinished;
	}

	/**
	 * @return list of interviewers.
	 */
	public List<Evaluator> getInterviewers() {
		return interviewers;
	}

	/**
	 * @param interviewerList set interviewers.
	 */
	public void setInterviewers(List<Evaluator> interviewerList) {
		this.interviewers = interviewerList;
	}

	/**
	 * @param interviewer interviewer to add.
	 */
	public void addInterviewer(Evaluator interviewer) {
		boolean exist = false;
		if (interviewer != null) {
			for (final Evaluator anInterviewer : interviewers) {
				if (anInterviewer.getEvaluatorId().equals(interviewer.getEvaluatorId())) {
					exist = true;
					break;
				}
			}
			if (!exist) {
				interviewers.add(interviewer);
				Assessment assToAdd = new Assessment(interviewer, this);
				assToAdd.setIsInterview(Boolean.TRUE);
				this.assessments.add(assToAdd);
			}
		}
	}

	/**
	 * @param interviewer interviewer to remove.
	 */
	public void removeInterviewer(Evaluator interviewer) {
		if (interviewer != null) {
			final Iterator<Evaluator> interviewerIterator = interviewers.iterator();
			while (interviewerIterator.hasNext()) {
				Evaluator anInterviewer = interviewerIterator.next();
				if (anInterviewer.getEvaluatorId().equals(interviewer.getEvaluatorId())) {
					interviewerIterator.remove();
				}
			}

			final Iterator<Assessment> assessmentIterator = assessments.iterator();
			while (assessmentIterator.hasNext()) {
				Assessment anAssessment = assessmentIterator.next();
				if (anAssessment.getEvaluator().getEvaluatorId().equals(interviewer.getEvaluatorId())
						&& anAssessment.getIsInterview()) {
					assessmentIterator.remove();
				}
			}
		}
	}

	/**
	 * @return all evaluators on this evaluation.
	 */
	public List<Evaluator> getEvaluators() {
		final List<Evaluator> evaluators = new ArrayList<Evaluator>();
		for (final Assessment assessment : assessments) {
			if (!assessment.getIsInterview()) {
				evaluators.add(assessment.getEvaluator());
			}
		}

		return evaluators;
	}

	/**
	 * Add evaluator and associated assessment. If the assessment for the given evaluator exists, nothing will be done.
	 * 
	 * @param evaluator evaluator to add.
	 */
	public void addEvaluator(Evaluator evaluator) {
		boolean exist = false;
		for (final Assessment assessment : assessments) {
			if (assessment.getEvaluator().getProfile().getProfileId().equals(evaluator.getProfile().getProfileId())
					&& !assessment.getIsInterview()) {
				exist = true;
				break;
			}
		}
		if (!exist) {
			Assessment assToAdd = new Assessment(evaluator, this);
			assToAdd.setIsInterview(Boolean.FALSE);
			this.assessments.add(assToAdd);
		}
	}

	/**
	 * @param evaluator evaluator to remove.
	 */
	public void removeEvaluator(Evaluator evaluator) {
		for (final Assessment assessment : assessments) {
			// if Evaluation is not serialized, remove from this list
			if ((assessment.getEvaluation().getEvaluationId() == null) && (this.evaluationId == null)) {
				if (assessment.getEvaluator().getEvaluatorId().equals(evaluator.getEvaluatorId())
						&& !assessment.getIsInterview()) {
					this.assessments.remove(assessment);
					break;
				}

			} else if (assessment.getEvaluation().getEvaluationId().equals(this.evaluationId)
					&& assessment.getEvaluator().getEvaluatorId().equals(evaluator.getEvaluatorId())
					&& !assessment.getIsInterview()) {
				this.assessments.remove(assessment);
				break;
			}
		}
	}

	/**
	 * @return evaluee.
	 */
	public Profile getEvaluee() {
		return evaluee;
	}

	/**
	 * @param profile evaluee to set.
	 */
	public void setEvaluee(Profile profile) {
		this.evaluee = profile;
	}

	/**
	 * @return evaluation session.
	 */
	public EvaluationSession getEvaluationSession() {
		return evaluationSession;
	}

	/**
	 * @return the event
	 */
	public Long getEventId() {
		return eventId;
	}

	/**
	 * @param anEventId the event to set
	 */
	public void setEventId(Long anEventId) {
		this.eventId = anEventId;
	}

	/**
	 * @param anEvaluationSession evaluation session to set.
	 */
	public void setEvaluationSession(EvaluationSession anEvaluationSession) {
		this.evaluationSession = anEvaluationSession;
	}

	/**
	 * @return the pdpReminders
	 */
	public Map<EvalCategory, PDPReminder> getPdpReminders() {
		return pdpReminders;
	}

	/**
	 * @param pdpReminderLis the pdpReminders to set
	 */
	public void setPdpReminders(Map<EvalCategory, PDPReminder> pdpReminderList) {
		this.pdpReminders = pdpReminderList;
	}

	/**
	 * @see com.levi9.hrm.model.base.AbstractBaseEntity#getExcludedAttributes()
	 */
	@Override
	protected String[] getExcludedAttributes() {
		return new String[] { "assessments", "interviewers" };
	}

	/**
	 * Retrieve conclusion from all interviews.
	 * 
	 * @return all conclusions.
	 */
	public String getConclusion() {
		final String separator = "<hr />";
		StringBuilder conclusion = new StringBuilder();
		for (Assessment assessment : assessments) {
			if (assessment.getIsInterview()) {
				conclusion.append(assessment.getConclusion());
				conclusion.append(separator);
			}
		}
		if (conclusion.length() < separator.length() + 1) {
			return "";
		}
		return conclusion.substring(0, conclusion.length() - separator.length());
	}
}
